package middleware

import (
	"errors"
	"github.com/appleboy/gin-jwt/v2"
	"github.com/gin-gonic/gin"
	"github.com/golang-module/carbon"
	"github.com/zhengchalei/gadmin/framework"
	"github.com/zhengchalei/gadmin/project/system/model"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"time"
)

var IdentityKey = "id"

var AuthMiddleware *jwt.GinJWTMiddleware

func init() {
	// the jwt middleware
	authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
		Realm:       "bJ0A0FKUsUgQAshX7PNCGead",
		Key:         []byte("DaZy4FeQyhQxwVYP0pkDAELu"),
		Timeout:     time.Hour * 8,
		MaxRefresh:  time.Hour * 8,
		IdentityKey: IdentityKey,
		LoginResponse: func(c *gin.Context, code int, token string, expire time.Time) {
			framework.Data(c, gin.H{
				"token":  token,
				"expire": carbon.Time2Carbon(expire).ToTimestamp() - carbon.Now().ToTimestamp(),
			})
		},
		PayloadFunc: func(data interface{}) jwt.MapClaims {
			if v, ok := data.(*model.SysUser); ok {
				return jwt.MapClaims{
					IdentityKey: v.ID,
					"nickname":  v.Nickname,
					"roles":     v.Roles,
					"dept":      v.Dept,
				}
			}
			return jwt.MapClaims{}
		},
		IdentityHandler: func(c *gin.Context) interface{} {
			claims := jwt.ExtractClaims(c)
			return &model.SysUser{
				Model: framework.Model{
					ID: uint(claims[IdentityKey].(float64)),
				},
			}
		},
		Authenticator: func(c *gin.Context) (interface{}, error) {
			var loginVals = &model.SysUser{}
			if err := c.ShouldBind(&loginVals); err != nil {
				return "", jwt.ErrMissingLoginValues
			}
			var r = &model.SysUser{}
			if err := framework.DB.Preload(clause.Associations).Where(loginVals).Find(loginVals).First(&r).Error; err != nil {
				if errors.Is(err, gorm.ErrRecordNotFound) {
					return nil, errors.New("用户名或密码错误")
				}
				return nil, err
			}
			return r, nil
		},
		Authorizator: func(data interface{}, c *gin.Context) bool {
			return true
		},
		Unauthorized: func(c *gin.Context, code int, message string) {
			if code == 401 {
				framework.CodeMsg(c, 401, "凭证已过期, 请重新登录", nil)
				return
			}
			if code == 403 {
				framework.CodeMsg(c, 401, "请求失败, 无权限访问", nil)
				return
			}
			framework.CodeMsg(c, 401, "请求失败", nil)
			return
		},
		// TokenLookup is a string in the form of "<source>:<name>" that is used
		// to extract token from the request.
		// Optional. Default value "header:Authorization".
		// Possible values:
		// - "header:<name>"
		// - "query:<name>"
		// - "cookie:<name>"
		// - "param:<name>"
		TokenLookup: "header: Authorization, query: token, cookie: jwt",
		// TokenLookup: "query:token",
		// TokenLookup: "cookie:token",

		// TokenHeadName is a string in the header. Default value is "Bearer"
		TokenHeadName: "Bearer",

		// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
		TimeFunc: time.Now,
	})

	if err != nil {
		framework.Log.Error("JWT Error", zap.Error(err))
	}

	// When you use jwt.New(), the function is already automatically called for checking,
	// which means you don't need to call it again.
	errInit := authMiddleware.MiddlewareInit()

	if errInit != nil {
		framework.Log.Error("authMiddleware.MiddlewareInit() Error", zap.Error(err))
	}
	AuthMiddleware = authMiddleware
}
